#ifndef XG_REFLECT_CPP
#define XG_REFLECT_CPP
///////////////////////////////////////////////////////////////////
#include "Reflect.h"

Object::~Object()
{
}
string Object::toString() const
{
	char buffer[16];

	sprintf(buffer, "%p", this);

	return buffer;
}
const char* Object::getClassName() const
{
#ifdef _MSC_VER
	return typeid(*this).name() + 6;
#else
	const char* str = typeid(*this).name();
	while (*str >= '0' && *str <= '9') str++;
	return str;
#endif
}

class ReflectAttrMap
{
	friend class ReflectHelper;

public:
	class Item
	{
	public:
		const char* type;
		const char* name;

		Item() : type(NULL), name(NULL)
		{
		}
	};

	class Data : public SpinMutex
	{
	public:
		int ofs;
		int end;
		ReflectKey key;
		mutable Item arr[10000];
		vector<ReflectItem> vec;

	public:
		Data(const Data& obj)
		{
			ofs = obj.ofs;
			end = obj.end;
			key = obj.key;
		}
		Data(ReflectKey _key = 0) : ofs(-1), end(-1), key(_key)
		{
		}

	public:
		vector<ReflectItem> getArrList()
		{
			if (end >= ofs) return vec;

			lock();

			vector<ReflectItem> res;

			for (int i = 0; i <= ofs; i++)
			{
				const Item& item = arr[i];

				if (item.name) res.push_back(ReflectItem(i, item.type, item.name));
			}

			vec = res;
			end = ofs;

			unlock();

			return res;
		}
		void add(int offset, const char* type, const char* name)
		{
			if (ofs >= offset) return;

			if (strcmp(type, "int") && strcmp(type, "bool") && strcmp(type, "float") && strcmp(type, "double") && strcmp(type, "string")) type = "object";

			assert(offset < ARR_LEN(arr));

			Item& item = arr[offset];

			lock();

			ofs = offset;
			item.type = type;
			item.name = name;

			unlock();
		}
	};

protected:
	SpinMutex mtx;
	vector<Data> arr[10000];

	vector<ReflectItem> getAttrList(ReflectKey key)
	{
		vector<Data>& vec = arr[(key >> 3) % ARR_LEN(arr)];

		for (Data& item : vec) if (key == item.key) return item.getArrList();

		return vector<ReflectItem>();
	}
	void add(Object* self, void* data, const char* type, const char* name)
	{
		ReflectKey key = ReflectHelper::GetKey(self);
		vector<Data>& vec = arr[(key >> 3) % ARR_LEN(arr)];

		for (Data& item : vec)
		{
			if (key == item.key)
			{
				item.add((char*)(data) - (char*)(self), type, name);

				return;
			}
		}

		{
			SpinLocker lk(mtx);

			for (Data& item : vec)
			{
				if (key == item.key)
				{
					item.add((char*)(data) - (char*)(self), type, name);

					return;
				}
			}

			vec.push_back(Data(key));

			vec.back().add((char*)(data) - (char*)(self), type, name);
		}
	}
	void add(Object* self, Object* data, const char* type, const char* name)
	{
		ReflectKey key = ReflectHelper::GetKey(data);
		vector<Data>& vec = arr[(key >> 3) % ARR_LEN(arr)];
		if (vec.size() > 0) add(self, (void*)(data), type, name);
	}
};

string ReflectItem::get(const void* obj) const
{
	char* dest = (char*)(obj) + offset;

	if (type == NULL || strcmp(type, "object") == 0) return "";

	if (strcmp(type, "int") == 0) return to_string(*(int*)(dest));

	if (strcmp(type, "bool") == 0) return *(bool*)(dest) ? "true" : "false";

	if (strcmp(type, "float") == 0) return to_string(*(float*)(dest));

	if (strcmp(type, "double") == 0) return to_string(*(double*)(dest));

	return *(string*)(dest);
}
bool ReflectItem::set(void* obj, int val) const
{
	char* dest = (char*)(obj) + offset;

	if (type == NULL || strcmp(type, "object") == 0) return false;

	if (strcmp(type, "int") == 0)
	{
		*(int*)(dest) = val;
	}
	else if (strcmp(type, "bool") == 0)
	{
		*(bool*)(dest) = val ? true : false;
	}
	else if (strcmp(type, "float") == 0)
	{
		*(float*)(dest) = val;
	}
	else if (strcmp(type, "double") == 0)
	{
		*(double*)(dest) = val;
	}
	else
	{
		*(string*)(dest) = to_string(val);
	}

	return true;
}
bool ReflectItem::set(void* obj, double val) const
{
	char* dest = (char*)(obj) + offset;

	if (type == NULL || strcmp(type, "object") == 0) return false;

	if (strcmp(type, "int") == 0)
	{
		*(int*)(dest) = val;
	}
	else if (strcmp(type, "bool") == 0)
	{
		*(bool*)(dest) = val < -0.000001 || val > 0.000001;
	}
	else if (strcmp(type, "float") == 0)
	{
		*(float*)(dest) = val;
	}
	else if (strcmp(type, "double") == 0)
	{
		*(double*)(dest) = val;
	}
	else
	{
		*(string*)(dest) = to_string(val);
	}

	return true;
}
bool ReflectItem::set(void* obj, const char* val) const
{
	char* dest = (char*)(obj) + offset;

	if (val == NULL || type == NULL || strcmp(type, "object") == 0) return false;

	if (strcmp(type, "string") == 0)
	{
		*(string*)(dest) = val;
	}
	else
	{
		if (*val == 0) return true;

		if (strcmp(type, "int") == 0)
		{
			*(int*)(dest) = atoi(val);
		}
		else if (strcmp(type, "bool") == 0)
		{
			*(bool*)(dest) = strcasecmp(val, "true") == 0;
		}
		else if (strcmp(type, "float") == 0)
		{
			*(float*)(dest) = atof(val);
		}
		else
		{
			*(double*)(dest) = atof(val);
		}
	}

	return true;
}

static ReflectAttrMap attrmap;

string ReflectHelper::GetAttrString(ReflectKey key)
{
	string res;
	string gap = ",";
	vector<ReflectItem> vec = attrmap.getAttrList(key);

	if (vec.empty()) return res;

	for (auto& item : vec) res += gap + item.getName();

	return res.substr(gap.length());
}
vector<ReflectItem> ReflectHelper::GetAttrList(ReflectKey key)
{
	return attrmap.getAttrList(key);
}
ReflectHelper::ReflectHelper(Object* self, void* data, const char* type, const char* name)
{
	attrmap.add(self, data, type, name);
}
ReflectHelper::ReflectHelper(Object* self, Object* data, const char* type, const char* name)
{
	attrmap.add(self, data, type, name);
}
///////////////////////////////////////////////////////////////////
#endif